Q1. 以下のナンバープレースを次のように、第一フィールドが行番号、第二フィールドが列番号、第三フィールドが区画(3x3のグリッドに適当に番号をつけたもの)、第四フィールドが値のファイルaに変換してください
from 第44回 シェル芸勉強会振り返り 問題と解答
Q1. 以下のナンバープレースを次のように、第一フィールドが行番号、第二フィールドが列番号、第三フィールドが区画(3x3のグリッドに適当に番号をつけたもの)、第四フィールドが値のファイルaに変換してください
#Q&A #awk #bash
code:bash
$ cat sudoku
53**7****
6**195***
*98****6*
8***6***3
4**8*3**1
7***2***6
*6****28*
***419**5
****8**79
$ head a
0 0 0 5
0 1 0 3
0 2 0 *
0 3 1 *
0 4 1 7
0 5 1 *
0 6 2 *
0 7 2 *
0 8 2 *
1 0 0 6
はじめの僕の解答がこれ
code:split_field.sh
cat sudoku | awk -v FS='' '{ for (i=0; i<NF; i++) { print NR-1, i, int(NR<3 ? i/3 : NR<6 ? (i/3)+3 : (i/3)+6), $(i+1)} }'
https://twitter.com/minyoruminyon/status/1187948757226147840
いけたのでは、と思ったけれどdiffしてみたらダメだったので間違ってる
まぁ3項演算子で分岐なんて汚いし汎用性が低いので悪いだろうとは思っていた
code:diff.sh
diff -u <(cat sudoku | awk -v FS='' '{ for (i=0; i<NF; i++) { print NR-1, i, int(NR<3 ? i/3 : NR<6 ? (i/3)+3 : (i/3)+6), $(i+1)} }') a
--- /proc/self/fd/11 2019-10-27 10:03:52.381830103 +0900
+++ a 2019-10-26 13:43:39.273177182 +0900
@@ -16,15 +16,15 @@
1 6 2 *
1 7 2 *
1 8 2 *
-2 0 3 *
-2 1 3 9
-2 2 3 8
-2 3 4 *
-2 4 4 *
-2 5 4 *
-2 6 5 *
-2 7 5 6
-2 8 5 *
+2 0 0 *
+2 1 0 9
+2 2 0 8
+2 3 1 *
+2 4 1 *
+2 5 1 *
+2 6 2 *
+2 7 2 6
+2 8 2 *
3 0 3 8
3 1 3 *
3 2 3 *
@@ -43,15 +43,15 @@
4 6 5 *
4 7 5 *
4 8 5 1
-5 0 6 7
-5 1 6 *
-5 2 6 *
-5 3 7 *
-5 4 7 2
-5 5 7 *
-5 6 8 *
-5 7 8 *
-5 8 8 6
+5 0 3 7
+5 1 3 *
+5 2 3 *
+5 3 4 *
+5 4 4 2
+5 5 4 *
+5 6 5 *
+5 7 5 *
+5 8 5 6
6 0 6 *
6 1 6 6
6 2 6 *
正しくはこう
3番目のフィールドの算出を間違えていた
code:split2.sh
cat sudoku | awk -v FS='' '{ for (i=0; i<NF; i++) { print NR-1, i, int((NR-1)/3)*3+int(i/3), $(i+1)} }'
3番目のフィールドの割り振り以下のような感じになる
code:txt
012 345 678
0 000 111 222
1 000 111 222
2 000 111 222
3 333 444 555
4 333 444 555
5 333 444 555
6 666 777 888
7 666 777 888
8 666 777 888
この配置の特徴は
3列ずつ値が1増加する
3行ずつ値が3増加する
と考えればあの時普通に算出できたはず
行の算出
行の番号はawkのNR(NumberRow)でとれる
よって行の開始はNR/3*3で出せる
code:row.sh
seq 9 | awk '{print NR, int(NR/3)*3}'
1 0
2 0
3 3
4 3
5 3
6 6
7 6
8 6
9 9
ただし、awkのNRは1から始まるのでNR-1しないといけない
code:row2.sh
seq 9 | awk '{print NR-1, int((NR-1)/3)*3}'
0 0
1 0
2 0
3 3
4 3
5 3
6 6
7 6
8 6
列の算出
for (i=0; i<NF; i++)でループしてるので列の番号はiに格納されてる
これも同様に3で割ればいい
code:col.sh
echo abcdefghi | awk -F '' '{ for (i=0; i<NF; i++) { print i, int(i/3) } }'
0 0
1 0
2 0
3 1
4 1
5 1
6 2
7 2
8 2
※こっちは最初から0始まりにしてるので-1は不要
以上を足せば求まる
code:block_base.sh
seq 9 | xargs -I@ bash -c 'seq 9 | xargs -IZ echo -n a; echo'
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
aaaaaaaaa
code:block.sh
seq 9 | xargs -I@ bash -c 'seq 9 | xargs -IZ echo -n a; echo' | awk -F '' '{ for (i=0; i<NF; i++) { printf "%s", int((NR-1)/3)*3 + int(i/3) } print "" }'
000111222
000111222
000111222
333444555
333444555
333444555
666777888
666777888
666777888
これら行、列、区画の番号を視覚的に表現するものをawkで書くと以下のようになる
code:index.sh
seq 9 |
xargs -I@ bash -c 'seq 9 | xargs -IZ echo -n a; echo' |
awk -F '' '{ for (i=0; i<NF; i++) { printf "%s", int((NR-1)/3)*3 + int(i/3) } print "" }' |
awk -F '' 'BEGIN{ printf " "; for (i=0; i<9; i++) { printf "%s", i } print; print "-+---------" } { print NR "|" $0}'
012345678
-+---------
1|000111222
2|000111222
3|000111222
4|333444555
5|333444555
6|333444555
7|666777888
8|666777888
9|666777888
数学的に考えれば普通に分かる問題のはずなのにパッとでてこなかったので、いよいよ数学勉強し直さないとやばいのではと危機感を覚えた